import random
import sys

import pygame
pygame.init()

# 回溯法生成完整数独
def is_valid(board, row, col, num):
    for i in range(9):
        if board[row][i] == num or board[i][col] == num:
            return False
    start_row, start_col = 3 * (row // 3), 3 * (col // 3)
    for i in range(start_row, start_row + 3):
        for j in range(start_col, start_col + 3):
            if board[i][j] == num:
                return False
    return True


def solve_sudoku(board):
    nums = list(range(1, 10))
    random.shuffle(nums)  # 打乱数字顺序
    for row in range(9):
        for col in range(9):
            if board[row][col] == 0:
                for num in nums:  # 使用打乱后的顺序
                    if is_valid(board, row, col, num):
                        board[row][col] = num
                        if solve_sudoku(board):
                            return True
                        board[row][col] = 0
                return False
    return True


def generate_sudoku():
    board = [[0]*9 for _ in range(9)]
    solve_sudoku(board)  # 填满整个棋盘
    # 随机挖空
    for _ in range(40):  # 控制难度
        r, c = random.randint(0,8), random.randint(0,8)
        while board[r][c] == 0:
            r, c = random.randint(0,8), random.randint(0,8)
        board[r][c] = 0
    return board


# 辅助函数：获取九宫格起始行列
def get_block_start(index):
    return (index // 3) * 3


# 初始化难度
difficulty = "简单"

# 初始化模式
mode = "common"

# 初始化选中格子
selected_cell = None

# 初始化选中的答案
answer = None

# 错误次数
error_times = 0


TOP_PADDING = 80
LR_PADDING = 5
screen = pygame.display.set_mode((372, 660))
pygame.display.set_caption("Sudoku")

font = pygame.font.SysFont("KaiTi", 24)
com_text_font = pygame.font.Font(None, 24)
answer_font = pygame.font.Font(None, 36)
note_font = pygame.font.Font(None, 24)

pygame.mixer.init()
# 加载音效
correct_sound = pygame.mixer.Sound("correct.mp3")
incorrect_sound = pygame.mixer.Sound("incorrect.mp3")
clock = pygame.time.Clock()
colors = {"common": (0, 0, 0), "comment": (0, 0, 255)}
board = generate_sudoku()
# 笔记
note = [[[0] for j in range(9)] for i in range(9)]
# 从键盘输入的数字
num = None


while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        if event.type == pygame.KEYDOWN:
            if selected_cell is not None:
                if event.key == pygame.K_LEFT:
                    if selected_cell[1] > 0:
                        selected_cell[1] -= 1
                elif event.key == pygame.K_RIGHT:
                    if selected_cell[1] < 8:
                        selected_cell[1] += 1
                elif event.key == pygame.K_DOWN:
                    if selected_cell[0] < 8:
                        selected_cell[0] += 1
                elif event.key == pygame.K_UP:
                    if selected_cell[0] > 0:
                        selected_cell[0] -= 1
                if board[selected_cell[0]][selected_cell[1]] == 0:
                    if event.key == pygame.K_1:
                        num = 1
                    elif event.key == pygame.K_2:
                        num = 2
                    elif event.key == pygame.K_3:
                        num = 3
                    elif event.key == pygame.K_4:
                        num = 4
                    elif event.key == pygame.K_5:
                        num = 5
                    elif event.key == pygame.K_6:
                        num = 6
                    elif event.key == pygame.K_7:
                        num = 7
                    elif event.key == pygame.K_8:
                        num = 8
                    elif event.key == pygame.K_9:
                        num = 9
            if event.key == pygame.K_n:
                mode = "note"
            elif event.key == pygame.K_c:
                mode = "common"
        elif event.type == pygame.MOUSEBUTTONDOWN:
            x, y = event.pos
            # 计算点击的是哪个格子
            row = (y - TOP_PADDING) // 40
            col = (x - LR_PADDING) // 40
            if 0 <= row < 9 and 0 <= col < 9:
                selected_cell = [row, col]
            if 0 <= selected_cell[1] < 9 and 460 + TOP_PADDING <= y <= 500 + TOP_PADDING:
                if board[selected_cell[0]][selected_cell[1]] == 0:
                    if is_valid(board, selected_cell[0], selected_cell[1], col + 1):
                        board[selected_cell[0]][selected_cell[1]] = col + 1
                        correct_sound.play()
                    else:
                        incorrect_sound.play()
    if num:
        if mode == "common":
            if is_valid(board, selected_cell[0], selected_cell[1], num):
                # 填入数字
                board[selected_cell[0]][selected_cell[1]] = num
                # 删除笔记
                note[selected_cell[0]][selected_cell[1]] = []
                # 播放音效
                correct_sound.play()
            else:
                incorrect_sound.play()
        elif mode == "note":
            if num not in note[selected_cell[0]][selected_cell[1]]:
                # 添加笔记
                note[selected_cell[0]][selected_cell[1]].append(num)
            else:
                # 减少笔记
                note[selected_cell[0]][selected_cell[1]].remove(num)
        # 清空数字
        num = None

    # 填充屏幕
    screen.fill((255, 255, 255))
    # 绘制状态栏
    text = font.render(difficulty, True, (0, 0, 0))
    screen.blit(text, (10, 10))
    text = font.render(f"错误：{error_times}/3", True, (0, 0, 0))
    screen.blit(text, (100, 10))
    time = pygame.time.get_ticks()
    text = font.render(f"{time // 60000:02d}:{time // 1000 % 60:02d}", True, (0, 0, 0))
    screen.blit(text, (270, 10))
    text = font.render("II", True, (0, 0, 0))
    screen.blit(text, (345, 10))
    # 绘制格子
    for i in range(10):
        if i % 3 == 0:
            pygame.draw.line(screen, (0, 0, 0), (i * 40 + LR_PADDING, TOP_PADDING), (i * 40 + LR_PADDING, 360 + TOP_PADDING), 2)
        else:
            pygame.draw.line(screen, (0, 0, 0), (i * 40 + LR_PADDING, TOP_PADDING), (i * 40 + LR_PADDING, 360 + TOP_PADDING), 1)
    for j in range(10):
        if j % 3 == 0:
            pygame.draw.line(screen, (0, 0, 0), (LR_PADDING, j * 40 + TOP_PADDING), (360 + LR_PADDING, j * 40 + TOP_PADDING), 2)
        else:
            pygame.draw.line(screen, (0, 0, 0), (LR_PADDING, j * 40 + TOP_PADDING), (360 + LR_PADDING, j * 40 + TOP_PADDING), 1)
    # 填充已填数字
    for n in range(9):
        for m in range(9):
            if board[n][m] != 0:
                if selected_cell is not None and board[n][m] == board[selected_cell[0]][selected_cell[1]]:
                    text = answer_font.render(str(board[n][m]), True, (0, 255, 0))
                    screen.blit(text, (m * 40 + LR_PADDING + 13, n * 40 + TOP_PADDING + 8))
                else:
                    text = answer_font.render(str(board[n][m]), True, (0, 0, 0))
                    screen.blit(text, (m * 40 + LR_PADDING + 13, n * 40 + TOP_PADDING + 8))
    # 填充笔记
    for n in range(9):
        for m in range(9):
            if note[n][m] is not []:
                for i in note[n][m]:
                    if selected_cell is not None and board[selected_cell[0]][selected_cell[1]] == i:
                        color = (0, 255, 0)
                    else:
                        color = (0, 0, 0)
                    if i == 1:
                        text = note_font.render(str(i), True, color)
                        screen.blit(text, (m * 40 + LR_PADDING + 2, n * 40 + TOP_PADDING + 1))
                    elif i == 2:
                        text = note_font.render(str(i), True, color)
                        screen.blit(text, (m * 40 + 10 + LR_PADDING + 6, n * 40 + TOP_PADDING + 2))
                    elif i == 3:
                        text = note_font.render(str(i), True, color)
                        screen.blit(text, (m * 40 + 20 + LR_PADDING + 9, n * 40 + TOP_PADDING + 2))
                    elif i == 4:
                        text = note_font.render(str(i), True, color)
                        screen.blit(text, (m * 40 + LR_PADDING + 2, n * 40 + 10 + TOP_PADDING + 4))
                    elif i == 5:
                        text = note_font.render(str(i), True, color)
                        screen.blit(text, (m * 40 + 10 + LR_PADDING + 6, n * 40 + 10 + TOP_PADDING + 5))
                    elif i == 6:
                        text = note_font.render(str(i), True, color)
                        screen.blit(text, (m * 40 + 20 + LR_PADDING + 9, n * 40 + 10 + TOP_PADDING + 5))
                    elif i == 7:
                        text = note_font.render(str(i), True, color)
                        screen.blit(text, (m * 40 + LR_PADDING + 2, n * 40 + 20 + TOP_PADDING + 6))
                    elif i == 8:
                        text = note_font.render(str(i), True, color)
                        screen.blit(text, (m * 40 + 10 + LR_PADDING + 6, n * 40 + 20 + TOP_PADDING + 7))
                    elif i == 9:
                        text = note_font.render(str(i), True, color)
                        screen.blit(text, (m * 40 + 20 + LR_PADDING + 9, n * 40 + 20 + TOP_PADDING + 7))
    # 如果有选中的格子，绘制蒙版
    if selected_cell is not None:
        row, col = selected_cell
        block_row = get_block_start(row)
        block_col = get_block_start(col)
        # 创建半透明表面
        overlay = pygame.Surface((40, 40), pygame.SRCALPHA)
        overlay.fill((128, 128, 128, 128))  # 白色，50%透明度

        # 绘制行蒙版
        for m in range(9):
            screen.blit(overlay, (col * 40 + LR_PADDING, m * 40 + TOP_PADDING))
        # 绘制列蒙版
        for n in range(9):
            screen.blit(overlay, (n * 40 + LR_PADDING, row * 40 + TOP_PADDING))
        # 绘制九宫格蒙版
        for r in range(block_row, block_row + 3):
            for c in range(block_col, block_col + 3):
                if r != row and c != col:
                    screen.blit(overlay, (c * 40 + LR_PADDING, r * 40 + TOP_PADDING))
    # 绘制其它控件
    pygame.draw.rect(screen, (0, 0, 0), (10, TOP_PADDING + 400, 30, 30), 2)
    # 绘制答案选项
    for a in range(1, 10):
        text = answer_font.render(str(a), True, (0, 0, 0))
        screen.blit(text, ((a - 1) * 40 + LR_PADDING + 13, 460 + TOP_PADDING))
    pygame.display.flip()
    clock.tick(60)
